package com.gitee.cirnochat.file.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.cirnochat.common.exception.BusinessException;
import com.gitee.cirnochat.common.exception.FileUploadException;
import com.gitee.cirnochat.common.model.enums.ErrorCode;
import com.gitee.cirnochat.common.utils.FileUploadValidationUtil;
import com.gitee.cirnochat.common.utils.FileUtil;
import com.gitee.cirnochat.common.utils.RedissonLockUtil;
import com.gitee.cirnochat.file.mapper.FileStorageRecordMapper;
import com.gitee.cirnochat.file.model.dto.FileUploadResultDTO;
import com.gitee.cirnochat.file.model.entity.FileStorageRecord;
import com.gitee.cirnochat.file.model.enums.FileStorageType;
import com.gitee.cirnochat.file.service.FileStorageRecordService;
import com.gitee.cirnochat.file.service.StorageService;
import com.gitee.cirnochat.file.service.function.UploadFailedCallBack;
import com.gitee.cirnochat.file.service.function.UploadSuccessCallBack;
import com.qcloud.cos.utils.Md5Utils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
* @author FlanChan
* @description 针对表【file_storage_record(文件储存记录)】的数据库操作Service实现
* @createDate 2025-04-23 11:56:08
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class FileStorageRecordServiceImpl extends ServiceImpl<FileStorageRecordMapper, FileStorageRecord>
    implements FileStorageRecordService {

    private final StorageService storageService;
    private final RedissonLockUtil redissonLockUtil;

    @Override
    public FileStorageRecord getFileStorageRecordByMD5(String md5) {
        return this.getOne(new LambdaQueryWrapper<FileStorageRecord>().eq(FileStorageRecord::getFileMd5, md5));
    }

    @Override
    public FileStorageRecord saveFileStorageRecord(String extension, String contentType, String url, String md5, File tempFile) {
        final FileStorageRecord record = extractFileStorageRecordEntity(extension, contentType, url, md5, tempFile);
        this.save(record);
        return record;
    }

    private FileStorageRecord extractFileStorageRecordEntity(String extension, String contentType, String url, String md5, File tempFile) {
        final FileStorageRecord record = new FileStorageRecord();
        record.setId(IdWorker.getId());
        record.setFileMd5(md5);
        record.setFileType(FileUtil.getFileTypeByContentType(contentType));
        record.setFileExt(extension);
        record.setFileUrl(url);
        record.setFileSize(tempFile.length());
        record.setStorageType(FileStorageType.COS);
        return record;
    }


    @Override
    public void uploadFile(MultipartFile file,
                           String uploadPathPrefix,
                           String previewHost,
                           boolean unique,
                           UploadSuccessCallBack successHandler,
                           UploadFailedCallBack failedHandler) {
        // 文件目录：根据业务、用户来划分
        String extension = FileUtil.getExtension(Objects.requireNonNull(file.getOriginalFilename()));
        String filename = FileUploadValidationUtil.getRandomUploadFileName() + '.' + extension;
        String uploadPath = uploadPathPrefix == null ?  FileUtil.getFileUploadPath(FileUtil.getFileTypeByContentType(Objects.requireNonNull(file.getContentType()))) + filename : uploadPathPrefix;
        //已上传的文件url
        AtomicReference<String> fileUrl = new AtomicReference<>();
        // 临时文件
        AtomicReference<File> tempFile = new AtomicReference<>();
        try (InputStream inputStream = file.getInputStream();
             BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
            bufferedInputStream.mark((int) file.getSize() + 1);
            final String md5 = new String(Md5Utils.computeMD5Hash(bufferedInputStream));
            // 计算MD5
            if (md5.isEmpty()) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件MD5计算失败");
            }
            //获取储存记录
            FileStorageRecord record = this.getFileStorageRecordByMD5(md5);
            if (record == null) {
                // 使用分布式锁，确保同一md5文件只上传一次
                redissonLockUtil.redissonDistributedLocks("FILE_UPLOAD_LOCK_KEY_" + md5, () -> {
                    FileStorageRecord innerRecord = this.getFileStorageRecordByMD5(md5);
                    if (innerRecord == null) {
                        try {
                            bufferedInputStream.reset();
                            log.info("创建临时文件：{}", filename);
                            tempFile.set(File.createTempFile(filename, null));
                            file.transferTo(tempFile.get());
                            log.info("临时文件路径：{}", tempFile.get().getPath());
                            log.info("文件上传路径：{}", uploadPath);
                            // 上传文件
                            storageService.uploadFile(uploadPath, tempFile.get());
                            fileUrl.set(uploadPath);
                            //保存文件记录
                            if (unique) {
                                //保存文件记录
                                innerRecord = this.saveFileStorageRecord(extension, file.getContentType(), previewHost + '/' + uploadPath, md5, tempFile.get());
                            }
                            successHandler.onUploadSuccess(new FileUploadResultDTO(innerRecord, FileUtil.generateRandomFileName()));
                        } catch (Exception e) {
                            log.error("文件上传失败，异常信息：{}", e.getMessage());
                            failedHandler.onUploadFailed(new FileUploadException(e.getMessage(), fileUrl.get()));
                        } finally {
                            if (tempFile.get() != null) {
                                // 删除临时文件
                                if (!tempFile.get().delete()) {
                                    log.error("删除临时文件失败，文件路径：{}", tempFile.get().getPath());
                                }
                            }
                        }
                    } else {
                        successHandler.onUploadSuccess(new FileUploadResultDTO(innerRecord, FileUtil.generateRandomFileName()));
                    }
                    return null;
                });
            } else {
                successHandler.onUploadSuccess(new FileUploadResultDTO(record, FileUtil.generateRandomFileName()));
            }
        } catch (Exception e) {
            log.error("文件上传失败，异常信息：{}", e.getMessage());
            //传递错误信息
            failedHandler.onUploadFailed(new FileUploadException(e.getMessage(), fileUrl.get()));
        } finally {
            if (tempFile.get() != null) {
                // 删除临时文件
                if (!tempFile.get().delete()) {
                    log.error("删除临时文件失败，文件路径：{}", tempFile.get().getPath());
                }
            }
        }
    }
}




